// trait是对未知类型self定义的方法集，该类型也可以访问同一个trait中定义的其他方法
// 对任何数据类型都可以实现 trait
// trait定义一组可以被共享的行为,只要实现了trait,就可以使用这种行为,类似于(接口)
//      可以通过trait以抽象的方式定义共享的行为
//      可以使用trait bounds指定泛型是任何拥有特定行为的类型

// 定义一个接口
trait HasArea {
    fn area(&self) -> f64;

    // 为trait定义一个默认实现
    fn print() -> () {
        println!("打印数据:{}", 1);
    }
}

trait PrintLen {
    fn print1() -> () {
        println!("这里是执行打印房屋长度方法");
    }
}

struct Circle {
    x: f64,
    y: f64,
    radius: f64,
}

// 实现这个接口
impl HasArea for Circle {
    // &self表示这个函数会将调用者的借代引用作为参数 
    fn area(&self) -> f64 {
        std::f64::consts::PI * (self.radius * self.radius)
    }

    // 如果不提供实现默认就会使用定义的默认实现
    fn print() -> () {
        println!("打印数据:{}", 2);
    }
}

// trait作为参数
// 参数类型必须是实现了GetInfomation trait的类型
pub fn print_info(item: impl HasArea) {
    item.print();
}

fn main() {
    let circle = Circle {
        x: 12,
        y: 11,
        radius: 0.5,
    };

    let sum = circle.area();
    circle.print();

    print_info(circle);
}

// 使用trait bound语法
// Rust中用于指定泛型类型参数所需的trait的一种方式，它还可以使用where关键字写成如下：

pub fn print_info1<T: HasArea>(item: T) {
    item.area();
}

pub fn print_info2<T>(item: T)
where
    T: HasArea,
{
    println!("执行方法", item.area());
}

// 通过+ 指定多个trait bound (多实现)
// 类型T必须实现PrintLen和HasArea
pub fn print_info3<T: PrintLen + HasArea>(item: T) {
    item.area();
    item.print1();
}

// 返回trait类型
pub fn print_info4() -> impl HasArea {
    Circle {
        x: 1,
        y: 2,
        radius: 3,
    }
}

// 注意方式返回的单一类型,
//  下面的代码将是错误的，无法编译通过，因为在编译时，返回的类型就确定为某一个实现了GetName trait的具体类型
pub fn produce_item_with_name(is_teacher: bool) -> impl GetName {
    let result = if is_teacher {
        Teacher {
            name: "alice".to_string(),
        }
    } else {
        Student {
            name: "alice".to_string(),
        }
    };
    result
}

struct Square {
    x: f64,
    y: f64,
    side: f64,
}

impl HasArea for Square {
    fn area(&self) -> f64 {
        self.side * self.side
    }
}

fn print_area<T: HasArea>(shape: T) {
    println!(" This  shape  has  an eara of {}", shape.area());
}


use std::fmt::Debug;

// 泛型的trait约束（多trait约束）
fn foo<T: Clone, K: Clone + Debug>(x: T, y: K) {
    x.clone();
    y.clone();
    println!("{:?}", y);
}

// 使用where关键字
fn bar<T, K>(x: T, y: K)
where
    K: Clone + Debug,
    T: Clone,
{
    x.clone();
    y.clone();
    println!("{:?}", y);
}

trait HasNumArea {
    fn area(&self) -> f64;
}

// trait与内置函数
impl HasNumArea for i32 {
    fn area(&self) -> f64 {
        *self as f64
    }
}


//孤儿规则
// 当你为某类型实现某 trait 的时候，必须要求类型或者 trait 至少有一个是在当前 crate 中定义的。你不能为第三方的类型实现第三方的 trait

// 特性的继承
trait HasCompute: HasArea {
    fn compute(&self) -> i32;
}

struct People;

impl HasArea for People {
    fn area(&self) -> f64 {
        return 32.0;
    }
}


impl HasCompute for People {
    fn compute(&self) -> i32 {
        return 33;
    }
}

// 泛型
// 泛型方法
fn makr_pired<T, U>(a: T, b: U) -> (T, U) {
    (a, b)
}

// 泛型结构
struct Point<T> {
    x: T,
    y: T,
}

struct GenericVal<T> {
    en_val: T,
}

impl<T> GenericVal<T> {
    fn value(&self) -> &T { &self.en_val }
}


//trait对象
// 为了解决trait只能返回单一的类型
// trait自身是不能当作数据类型来使用,但trait对象可以当作数据类型使用
// 可以将实现了trait A 类型的b,c,d当作trait A的trait对象来使用
// 使用trait对象时,基本都是以引用的方式使用,所以使用通常是引用符号加dyn关键字 &dyn
// trait是指使用指针封装了的trait比如说&SomeTrait和Box<SomeTrait>
trait Foo1 {
    fn method(&self) -> String;
}

// trait对象动态分发的原理
// 对于trait对象,需要说明以下几点
//   trait对象不固定,这是因为trait A,类型b可以实现trait A,类型c也可以实现trait A,因此trait对象的大小是无法确定的
//   使用trait对象时,总是使用它们的引用的方式:
//         虽然trait对象没有固定的大小,但它的引用类型的大小固定,它是由两个指针组成,因此占两个指针的大小
//          一个指针指向具体类型的实例
//          另一个指针指向一个虚表vtable,vtable中保存了实例对于可以调用的实现于trait的方法，当调用方法时，直接从vtable中找到方法并调用
//          trait对象的引用方式有很多种，例如对trait A,其trait类型的引用可以是 &dyn A,Box<dyn A>,Rc<dyn A>

trait Vehicle {
    fn run(&self);
}

// Car是实现了Vehicle trait的类型
// 只有一个字段表示车牌号
struct Car(u32);

impl Vehicle for Car {
    fn run(&self) {
        println!("Car {:?} run ... ", self.0);
    }
}

// truck是实现了Vehicle trait的类型
// 只有一个字段表示车牌号
struct Truck(u32);

impl Vehicle for Truck {
    fn run(&self) {
        println!("Truck {:?} run ... ", self.0);
    }
}

fn main() {
    let car = Car(1001);
    let truck = Truck(1002);

    let vehicle1: &dyn Vehicle = &car;
    let vehicle2: &dyn Vehicle = &truck;

    vehicle1.run();
    vehicle2.run();
}

//  trait对象要求对象安全
//  只有对象安全的trait才可以组成trait对象（两点要求）
//         返回值类型不为Self
//         方法没有任何泛型类型参数

// 不允许返回Self，是因为trait对象在产生时，原来的具体的类型会被抹去，Self具体是什么类型不知道，所以编译会报错；
// 不允许携带泛型参数，是因为Rust用带泛型的类型在编译时会做单态化，而trait对象是运行时才确定，
//     即一个运行时才能确定的东西里又包含一个需要在编译时确定的东西，相互冲突，必然是不行的


// 常见的trait
// std::fmt::Display: 格式化打印用户友好字符串。
// std::fmt::Debug: 格式化打印调试字符串。
// std::cmp::PartialEq: 比较值相等。
// std::cmp::PartialOrd: 比较值顺序。
// std::cmp::Eq: 类型完全相等关系。
// std::cmp::Ord: 类型完全顺序关系。
// std::clone::Clone: 创建类型副本。
// std::ops::Add: 定义加法操作。
// std::ops::Mul: 定义乘法操作。
// std::iter::Iterator: 实现迭代器。

// `T` 的泛型 trait
trait DoubleDrop<T> {
    // 定义一个调用者的方法，接受一个额外的参数 `T`，但不对它做任何事。
    fn double_drop(self, _: T);
}

// 对泛型的调用者类型 `U` 和任何泛型类型 `T` 实现 `DoubleDrop<T>` 。
impl<T, U> DoubleDrop<T> for U {
    // 此方法获得两个传入参数的所有权，并释放它们。
    fn double_drop(self, _: T) {}
}


//约束
// 使用泛型时，参数类型常常必须使用trait作为约束来规定类型应实现那些功能
// T必须实现display
fn printer<T: Display>(t: T) {}

struct S<T: Display>(T);


#[derive(Debug)]
struct Rectangle {
    length: f64,
    height: f64,
}

// 约束的作用是泛型实例可以作为约束的trait方法
trait HasAreaV1 {
    // 方法
    fn area(&self) -> f64;
}

impl HasArea for Rectangle {
    fn area(&self) -> f64 { self.length * self.height }
}
// 都可访问 `HasArea` 的 `area` 函数
fn area<T: HasArea>(t: &T) -> f64 { t.area() }

// 多重约束 用+连接，类型使用，隔开
fn compare_prints<T: Debug + Display>(t: &T) {
    println!("Debug: `{:?}`", t);
    println!("Display: `{}`", t);
}

fn compare_types<T: Debug, U: Debug>(t: &T, u: &U) {
    println!("t: `{:?}`", t);
    println!("u: `{:?}`", u);
}
// where可以用于任意类型的限定，而不局限于类型参数本身

impl<A: TraitB + TraitC, D: TraitE + TraitF> MyTrait<A, D> for YourType {}

impl<A, D> MyTrait<A, D> for YourType
where
    A: TraitB + TraitC,
    D: TraitE + TraitF,
{}


impl Foo1 for i32 {
    fn method(&self) -> String {
        format!("i32:{}", *self)
    }
}

// x:&Foo1 x是一个trait对象，这里使用是用指针，内存大小不确定,指针大小是确定的
fn do_something(x: &Foo1) {
    x.method();
}


// trait对象的实现
// 并不是所有的trait都能作为trait对象使用的
// &sometrait类型和普通的指针不同， 不仅包含指向真实对象的指针，还包括一个指向虚函数表的指针
// pub struct TraitObject {
//     pub data: *mut (), 指向实际类型实例的指针
//     pub vtable: *mut (), 指向实际类型对于该trait的实现的虚函数表
// }


// 如果一个trait方法是object safe的，它需要满足：
//     方法有Self: Sized约束， 或者
//     同时满足以下所有条件：
//         没有泛型参数
//         不是静态函数
//         除了self之外的其它参数和返回值不能使用Self类型

// 如果一个trait是object-safe的，它需要满足：

//     所有的方法都是object-safe的，并且
//     trait 不要求 Self: Sized 约束


fn main() {
    // derive属性  为了避免重复的实现一些trait提供一个属性
    #[derive(Debug)]
    struct Device;

    let square = Square {
        x: 1.0,
        y: 1.0,
        side: 1.0,
    };

    print_area(square);

    5.area();

    let origin_num = Point {
        x: 1.0,
        y: 1.0,
    };

    println!("{:?}", Device);
    let y = 32;
    do_something(&y);
    do_something(&y)
}
